package com.tornainbow;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author mcrysler
 */
public final class Record implements Serializable {
    // Define a logger for this class
    private static final Logger logger = LoggerFactory.getLogger(Record.class);
    
    // Define private class variables
    private volatile String classification = "";
    private volatile String accessControls = "";
    private Collection<Field> fields = Collections.synchronizedList(new ArrayList<Field>());
    private AtomicReference<byte[]> payload = new AtomicReference(new byte[0]);
    
    // Define the key for the UUID
    public static final String KEY_UUID = "UUID";
    
    /**
     * The required no-argument constructor used for serialization
     */
    public Record() {
        super();
        this.addField(new Field(KEY_UUID, UUID.randomUUID().toString()));
    }
    
    /**
     * Sets the classification to associate to this object
     * @param classification The classification to associate to this object
     */
    public void setClassification(String classification) {
        this.classification = classification;
        logger.debug("Set classification to: {}", this.classification);
    }
    
    /**
     * Returns the classification associated to this object
     * @return The classification associated to this object
     */
    public String getClassification() {
        return this.classification;
    }
    
    /**
     * Sets the access controls to associate to this object
     * @param accessControls The access controls to associate to this object
     */
    public void setAccessControls(String accessControls) {
        this.accessControls = accessControls;
        logger.debug("Set access controls to: {}", this.accessControls);
    }
    
    /**
     * Returns the access controls associated to this object
     * @return The access controls associated to this object
     */
    public String getAccessControls() {
        return this.accessControls;
    }
    
    /**
     * Sets the payload to associate to this object
     * @param payload 
     */
    public void setPayload(byte[] payload) {
        this.payload = new AtomicReference(payload);
    }
    
    /**
     * Returns the payload associated to this object
     * @return The payload associated to this object
     */
    public byte[] getPayload() {
        return this.payload.get();
    }
    
    /**
     * Adds a Field object to the set of fields associated to this object
     * @param field The Field object to associate to this object
     */
    public synchronized void addField(Field field) {
        this.fields.add(field);
    }
    
    /**
     * Returns the set of Field objects associated to this object
     * @return The set of Field objects associated to this object
     */
    public synchronized Collection<Field> getAllFields() {
        return this.fields;
    }
    
    /**
     * Returns the set of Field objects whose field name equals the given name
     * @param name The name that will determine which Field objects are returned
     * @return A list of Field objects whose name equals the given name
     */
    public synchronized Collection<Field> getFieldsByFieldName(String name) {
        Collection<Field> matchingFields = new ArrayList<Field>();
        Iterator<Field> iter = this.fields.iterator();
        while (iter.hasNext()) {
            Field field = iter.next();
            if (field.getName().equalsIgnoreCase(name)) {
                matchingFields.add(field);
            }
        }
        return matchingFields;
    }
    
    /**
     * Returns the set of Field objects whose canonicalized name equals the given name
     * @param name The name that will determine which Field objects are returned
     * @return A list of Field objects whose name equals the given name
     */
    public synchronized Collection<Field> getFieldsByCanonicalizedName(String name) {
        Collection<Field> matchingFields = new ArrayList<Field>();
        Iterator<Field> iter = this.fields.iterator();
        while (iter.hasNext()) {
            Field field = iter.next();
            if (field.getName().equalsIgnoreCase(name)) {
                matchingFields.add(field);
            }
        }
        return matchingFields;
    }
    
    /**
     * Removes the set of Field objects whose name equals the given name and
     * returns the new collection
     * @param name The name that will determine which Field objects are removed
     */
    public synchronized Collection<Field> removeFieldsByFieldName(String name) {
        // Define a collection to hold the Field objects to remove
        Collection<Field> fieldsToRemove = new ArrayList<Field>();
        
        // Iterate over the collection of Field objects and check to see if the
        // given field name matches the specified name
        Iterator<Field> iter1 = this.fields.iterator();
        while (iter1.hasNext()) {
            Field field = iter1.next();
            if (field.getName().equalsIgnoreCase(name)) {
                fieldsToRemove.add(field);
            }
        }
        
        // Iterate over the collected Field objects and remove them from the
        // overall collection
        Iterator<Field> iter2 = fieldsToRemove.iterator();
        while (iter2.hasNext()) {
            this.fields.remove(iter2.next());
        }
        
        // Return the new collection of Field objects
        return this.fields;
    }
    
    /**
     * Removes the set of Field objects whose canonicalized name equals the 
     * given name and returns the new collection
     * @param name The name that will determine which Field objects are removed
     */
    public synchronized Collection<Field> removeFieldsByCanonicalizedName(String name) {
        // Define a collection to hold the Field objects to remove
        Collection<Field> fieldsToRemove = new ArrayList<Field>();
        
        // Iterate over the collection of Field objects and check to see if the
        // given field name matches the specified name
        Iterator<Field> iter1 = this.fields.iterator();
        while (iter1.hasNext()) {
            Field field = iter1.next();
            if (field.getCanonicalizedName().equalsIgnoreCase(name)) {
                fieldsToRemove.add(field);
            }
        }
        
        // Iterate over the collected Field objects and remove them from the
        // overall collection
        Iterator<Field> iter2 = fieldsToRemove.iterator();
        while (iter2.hasNext()) {
            this.fields.remove(iter2.next());
        }
        
        // Return the new collection of Field objects
        return this.fields;
    }
    
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("====================================================").append("\n");
        builder.append("Record Classification: ").append(this.getClassification()).append("\n");
        builder.append("Record Access Controls: ").append(this.getAccessControls()).append("\n");
        builder.append("Fields:").append("\n");
        Iterator<Field> iter = this.getAllFields().iterator();
        while (iter.hasNext()) {
            Field field = iter.next();
            builder.append("----------------------------------------------------").append("\n");
            builder.append(field);
        }
        return builder.toString();
    }
}
